package com;

import java.io.BufferedReader;
import java.io.IOException;
import java.net.*;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Iterator;
import java.util.Set;

public class NonBlockingSingleFileHTTPServer {

    private final static int DEFAULT_PORT = 1118;

    private int port;

    private ByteBuffer contentBuffer;

    public NonBlockingSingleFileHTTPServer(ByteBuffer data, String encoding, String MIIMEType, int port) {
        this.port = port;
        String header = "HTTP/1.0 200 OK\r\n"
                + "Server: NonBlockingSingleFileHTTPServer2.0\r\n"
                + "Content-length: " + data.limit() + "\r\n"
                + "Content-type: " + MIIMEType + "\r\n\r\n";
        byte[] headerData = header.getBytes(Charset.forName("US-ASCII"));

        ByteBuffer buffer = ByteBuffer.allocate(data.limit() + headerData.length);
        buffer.put(headerData);
        buffer.put(data);
        buffer.flip();
        this.contentBuffer = buffer;
    }

    public void start(){

        try (ServerSocketChannel channel = ServerSocketChannel.open()){

            ServerSocket serverSocket = channel.socket();

            SocketAddress addr = new InetSocketAddress(port);

            serverSocket.bind(addr);

            channel.configureBlocking(false);

            Selector selector = Selector.open();

            channel.register(selector, SelectionKey.OP_ACCEPT);

            while (true){

                selector.select();

                Set<SelectionKey> set = selector.selectedKeys();

                Iterator<SelectionKey> iter = set.iterator();

                while (iter.hasNext()){

                    SelectionKey key = iter.next();

                    iter.remove();

                    if(key.isAcceptable()){

                        ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key.channel();

                        SocketChannel client = serverSocketChannel.accept();

                        client.configureBlocking(false);

                        client.register(selector,SelectionKey.OP_READ);

                    }else if(key.isReadable()){

                        SocketChannel client = (SocketChannel) key.channel();

                        ByteBuffer buffer = ByteBuffer.allocate(4096);

                        client.read(buffer);

                        key.interestOps(SelectionKey.OP_WRITE);

                        key.attach(contentBuffer.duplicate());

                    }else if(key.isWritable()){

                        SocketChannel client = (SocketChannel) key.channel();

                        ByteBuffer buffer = (ByteBuffer) key.attachment();

                        if(buffer.hasRemaining()){

                            client.write(buffer);

                        }else{

                            client.close();

                        }


                    }

                }

            }

        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    public static void main(String[] args) {

        if(args.length == 0){

            System.out.println("Usage: java NonBlockingSingleFileHTTPServer file port encoding");

        }

        try {
            String contentType = URLConnection.getFileNameMap().getContentTypeFor(args[0]);

            Path file = FileSystems.getDefault().getPath(args[0]);

            byte[] data = Files.readAllBytes(file);

            ByteBuffer input = ByteBuffer.wrap(data);

            int port;

            try {
                port = Integer.parseInt(args[1]);
                if(port<1 || port>65535) port = DEFAULT_PORT;
            }catch (RuntimeException e){
                port = DEFAULT_PORT;
            }

            String encoding = "UTF-8";

            if(args.length > 2)
                encoding = args[2];

            NonBlockingSingleFileHTTPServer server = new NonBlockingSingleFileHTTPServer(input,encoding,contentType,port);

            server.start();


        } catch (IOException e) {
            e.printStackTrace();
        }


    }
}
